/**
  ******************************************************************************
  * @文件     spim.c
  * @作者     hero_cao
  * @版本     V1.0.0
  * @日期     2020/10/29
  * @概要     flash读写
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "./spim/spim.h"
#include <string.h>
#include "./board/bsp.h"

/* Private variables ---------------------------------------------------------*/
u8 WriteBuffer[SPIM_PAGE_SIZE];
u8 ReadBuffer[SPIM_PAGE_SIZE];


/**
  * @brief  初始化外部spiflash
  * @param  无
  * @retval 无
  */
void flash_init_spim(void)
{
	gpio_init_type gpio_init_struct;
	
	crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
	crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
	crm_periph_clock_enable(CRM_IOMUX_PERIPH_CLOCK, TRUE);

	gpio_default_para_init(&gpio_init_struct);
	
	gpio_pin_remap_config(XMC_NADV_GMUX, TRUE);		//关闭XMC_NADV功能(PB7被占用)
	
	/* configure the CS pin */
	gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MODERATE;
	gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
	gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
	gpio_init_struct.gpio_pins = GPIO_PINS_8;
	gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
	gpio_init(GPIOA, &gpio_init_struct);

	/* configure the IO pin */
	gpio_init_struct.gpio_pins = GPIO_PINS_1 | GPIO_PINS_6 | GPIO_PINS_7 | GPIO_PINS_10 | GPIO_PINS_11;
	gpio_init(GPIOB, &gpio_init_struct);

	gpio_pin_remap_config(EXT_SPIM_GMUX_1001, TRUE);

	/* configure param */
	flash_spim_model_select(FLASH_SPIM_MODEL1);

	/* unlock the spim flash program erase controller */
	while(flash_flag_get(FLASH_SPIM_OBF_FLAG));
	flash_spim_unlock();
	while(FLASH->ctrl3_bit.oplk);

	/* if the data written to spim flash need to be scrambled, please specify the scrambled range */
	flash_spim_encryption_range_set(0x0); 
}

/**
  * @brief  从外部flash读取一页数据
  * @param  adr: 起始页地址
  * @param  sz: 页大小
  * @param  buf: 读到的数据
  * @retval 无
  */
void ReadPage (unsigned long adr, unsigned long sz, unsigned char *buf)
{
    while (sz) {
        *(uint32_t *)buf = *(uint32_t *)adr;    
        sz -= sizeof(uint32_t);
        adr += sizeof(uint32_t);
        buf += sizeof(uint32_t);
    }   
}

/**
  * @brief  测试外部flash
  * @param  无
  * @retval 无
  */
void SPIM_test(void)
{
    u16 i=0;
    
    for(i=0;i<SPIM_PAGE_SIZE;i++) {
        WriteBuffer[i]=i%256;
    }

    flash_sector_erase(SPIM_TEST_ADDR);
    
    memset(ReadBuffer,0,SPIM_PAGE_SIZE);
    ReadPage(SPIM_TEST_ADDR, SPIM_PAGE_SIZE, ReadBuffer);
    
    for(i=0;i<SPIM_PAGE_SIZE;i++) {
        if(ReadBuffer[i]!=0xff) {
            return;
        }    
    }

	flash_write(SPIM_TEST_ADDR+i,(uint8_t *)WriteBuffer,SPIM_PAGE_SIZE);
    
    memset(ReadBuffer,0,SPIM_PAGE_SIZE);
    ReadPage(SPIM_TEST_ADDR, SPIM_PAGE_SIZE, ReadBuffer);
    
    for(i=0;i<SPIM_PAGE_SIZE;i++) {
        if(ReadBuffer[i]!=WriteBuffer[i]) {
            return;
        }    
    }
}

/**
  * @brief  flash页擦除
  * @param  page_addr-页地址
  * @retval 0-成功，-1-失败
  */
int flash_page_erase(uint32_t page_addr)
{
    flash_status_type status;

    __disable_irq();
    flash_spim_unlock();

    flash_flag_clear(FLASH_OPERATE_BUSY | FLASH_PROGRAM_ERROR | FLASH_EPP_ERROR |
        FLASH_OPERATE_DONE | FLASH_OPERATE_TIMEOUT);	

    status = flash_sector_erase(page_addr);

    flash_spim_lock();
    __enable_irq();

    return status != FLASH_OPERATE_DONE ? -1 : 0;
}

/**
  * @brief  flash写数据
  * @param  addr-要写入的地址，data-要写入的数据，data_len-要写入数据的长度
  * @retval 写入数据长度，其他-写入失败
  */
int flash_write(uint32_t addr, uint8_t *data, uint32_t data_len)
{
    uint32_t word, count, i;
    int ret = -1;

    __disable_irq();
    flash_spim_unlock();

    flash_flag_clear(FLASH_OPERATE_BUSY | FLASH_PROGRAM_ERROR | FLASH_EPP_ERROR |
        FLASH_OPERATE_DONE | FLASH_OPERATE_TIMEOUT);

    count = data_len / 4;
    if (data_len % 4)
        count++;
    for (i = 0 ; i < count ; i++) {
        word = *((uint32_t *)data + i);
        if (flash_word_program(addr, word) != FLASH_OPERATE_DONE)
            goto Exit;

        addr += 4;
    }

    ret = data_len;
Exit:
    flash_spim_lock();
    __enable_irq();

    return ret;
}

/**
  * @brief  flash读数据
  * @param  addr-要读取的地址，data-要读取的数据，data_len-要读取数据的长度
  * @retval 读取数据长度
  */
int flash_read(uint32_t addr, uint8_t *data, uint32_t data_len)
{
    uint32_t i;

    for(i = 0; i < data_len; i++)
        data[i] = *(uint8_t *) (addr + i);

    return i;
}


/* FILE END */
